from typing import List, Optional, Dict, Any
from sqlalchemy.orm import Session
from src.models.database.models import Plate, Position
from pydantic import BaseModel
from datetime import datetime

class PlateCreate(BaseModel):
    name: str
    capacity: int
    description: str = ""

class PlateUpdate(BaseModel):
    capacity: Optional[int] = None
    description: Optional[str] = None

class PlateResponse(BaseModel):
    id: int
    name: str
    capacity: int
    description: str
    occupied_count: int
    available_count: int
    disabled_count: int
    
    class Config:
        from_attributes = True

class PlateService:
    def __init__(self, db: Session):
        self.db = db
    
    def get_all_plates(self) -> List[PlateResponse]:
        """获取所有料盘"""
        plates = self.db.query(Plate).all()
        result = []
        
        for plate in plates:
            # 统计料位状态
            positions = self.db.query(Position).filter(Position.plate_id == plate.id).all()
            occupied_count = sum(1 for p in positions if p.sample_id and p.sample_id != "" and p.sample_id != "#999")
            disabled_count = sum(1 for p in positions if p.sample_id == "#999")
            available_count = len(positions) - occupied_count - disabled_count
            
            result.append(PlateResponse(
                id=plate.id,
                name=plate.name,
                capacity=plate.capacity,
                description=plate.description,
                occupied_count=occupied_count,
                available_count=available_count,
                disabled_count=disabled_count
            ))
        
        return result
    
    def get_plate_by_name(self, name: str) -> Optional[Plate]:
        """根据名称获取料盘"""
        return self.db.query(Plate).filter(Plate.name == name).first()
    
    def create_plate(self, plate_data: PlateCreate) -> Plate:
        """创建料盘"""
        # 检查名称是否已存在
        existing = self.get_plate_by_name(plate_data.name)
        if existing:
            raise ValueError(f"料盘名称 '{plate_data.name}' 已存在")
        
        # 创建料盘
        plate = Plate(
            name=plate_data.name,
            capacity=plate_data.capacity,
            description=plate_data.description
        )
        self.db.add(plate)
        self.db.flush()  # 获取ID
        
        # 创建料位
        for i in range(plate.capacity):
            position = Position(
                plate_id=plate.id,
                position_index=i,
                sample_id="",
                status="available"
            )
            self.db.add(position)
        
        self.db.commit()
        self.db.refresh(plate)
        return plate
    
    def update_plate(self, name: str, plate_data: PlateUpdate) -> Optional[Plate]:
        """更新料盘"""
        plate = self.get_plate_by_name(name)
        if not plate:
            return None
        
        # 更新基本信息
        if plate_data.description is not None:
            plate.description = plate_data.description
        
        # 更新容量（需要调整料位）
        if plate_data.capacity is not None and plate_data.capacity != plate.capacity:
            current_positions = self.db.query(Position).filter(Position.plate_id == plate.id).all()
            current_count = len(current_positions)
            
            if plate_data.capacity > current_count:
                # 增加料位
                for i in range(current_count, plate_data.capacity):
                    position = Position(
                        plate_id=plate.id,
                        position_index=i,
                        sample_id="",
                        status="available"
                    )
                    self.db.add(position)
            elif plate_data.capacity < current_count:
                # 减少料位时，检查是否有超出范围的料位被占用
                occupied_positions = self.db.query(Position).filter(
                    Position.plate_id == plate.id,
                    Position.position_index >= plate_data.capacity,
                    Position.sample_id != "",
                    Position.sample_id != "#999"
                ).all()
                
                if occupied_positions:
                    # 有占用的料位超出新容量范围
                    occupied_samples = [pos.sample_id for pos in occupied_positions]
                    raise ValueError(f"CAPACITY_CONFLICT:无法减少容量，料位 {[pos.position_index for pos in occupied_positions]} 中有样品: {occupied_samples}。请选择是否清空料盘后重新设置容量。")
                
                # 删除超出范围的空料位和禁用料位
                positions_to_remove = self.db.query(Position).filter(
                    Position.plate_id == plate.id,
                    Position.position_index >= plate_data.capacity
                ).all()
                
                for pos in positions_to_remove:
                    self.db.delete(pos)
            
            plate.capacity = plate_data.capacity
        
        self.db.commit()
        self.db.refresh(plate)
        return plate
    
    def update_plate_with_reset(self, name: str, plate_data: PlateUpdate) -> Optional[Plate]:
        """更新料盘（强制清空所有料位）"""
        plate = self.get_plate_by_name(name)
        if not plate:
            return None
        
        # 更新基本信息
        if plate_data.description is not None:
            plate.description = plate_data.description
        
        # 更新容量（强制重置所有料位）
        if plate_data.capacity is not None and plate_data.capacity != plate.capacity:
            # 删除所有现有料位
            existing_positions = self.db.query(Position).filter(Position.plate_id == plate.id).all()
            for pos in existing_positions:
                self.db.delete(pos)
            
            # 先提交删除操作，避免约束冲突
            self.db.flush()
            
            # 创建新的料位
            for i in range(plate_data.capacity):
                position = Position(
                    plate_id=plate.id,
                    position_index=i,
                    sample_id="",
                    status="available"
                )
                self.db.add(position)
            
            plate.capacity = plate_data.capacity
        
        self.db.commit()
        self.db.refresh(plate)
        return plate
    
    def delete_plate(self, name: str) -> bool:
        """删除料盘"""
        plate = self.get_plate_by_name(name)
        if not plate:
            return False
        
        # 检查是否有样品
        occupied_positions = self.db.query(Position).filter(
            Position.plate_id == plate.id,
            Position.sample_id != "",
            Position.sample_id != "#999"
        ).count()
        
        if occupied_positions > 0:
            raise ValueError(f"料盘 '{name}' 中还有样品，无法删除")
        
        self.db.delete(plate)  # 级联删除料位
        self.db.commit()
        return True
    
    def get_plate_positions(self, name: str) -> List[Dict[str, Any]]:
        """获取料盘的所有料位信息"""
        plate = self.get_plate_by_name(name)
        if not plate:
            return []
        
        positions = self.db.query(Position).filter(
            Position.plate_id == plate.id
        ).order_by(Position.position_index).all()
        
        result = []
        for pos in positions:
            result.append({
                "position_index": pos.position_index,
                "sample_id": pos.sample_id,
                "status": self._get_position_status(pos.sample_id),
                "updated_at": pos.updated_at.isoformat() if pos.updated_at else None
            })
        
        return result
    
    def get_available_positions(self, name: str) -> List[int]:
        """获取料盘的可用料位索引列表"""
        plate = self.get_plate_by_name(name)
        if not plate:
            return []
        
        positions = self.db.query(Position).filter(
            Position.plate_id == plate.id,
            Position.sample_id == ""
        ).order_by(Position.position_index).all()
        
        return [pos.position_index for pos in positions]
    
    def _get_position_status(self, sample_id: str) -> str:
        """根据样品ID判断料位状态"""
        if sample_id == "#999":
            return "disabled"
        elif sample_id == "" or sample_id is None:
            return "available"
        else:
            return "occupied"
    
    def clear_all_positions(self, name: str, task_id: str = "", operator: str = "system") -> bool:
        """批量清空料盘的所有料位（保留禁用状态）"""
        plate = self.get_plate_by_name(name)
        if not plate:
            return False
        
        # 获取所有料位
        positions = self.db.query(Position).filter(Position.plate_id == plate.id).all()
        
        # 统计将要清空的样品数量
        occupied_count = 0
        for pos in positions:
            if pos.sample_id and pos.sample_id != "" and pos.sample_id != "#999":
                # 清空占用的料位（保留禁用状态）并记录任务信息
                pos.sample_id = ""
                pos.status = "available"
                pos.task_id = task_id
                pos.task_type = "clear"
                pos.operator = operator
                pos.operation_time = datetime.now()
                pos.updated_at = datetime.now()
                occupied_count += 1
        
        # 即使没有样品也返回成功
        self.db.commit()
        return True 